package ru.ifmo.itmokis.sim;

import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import ru.ifmo.itmokis.util.DataContext;
import ru.ifmo.itmokis.Facade;
import ru.ifmo.itmokis.domain.Order;
import ru.ifmo.itmokis.domain.OrderedProduct;
import ru.ifmo.itmokis.domain.Time;
import ru.ifmo.itmokis.util.Config;
import ru.ifmo.itmokis.util.EventDriven;
import ru.ifmo.itmokis.util.Randoms;

/**
 *
 * @author Ivan Stepuk
 */
public final class OrderFactory implements ClockListener, EventDriven {

    private Config config;
    private Randoms randoms;
    private Facade facade;
    private DataContext dataContext;
    // ^ Dependencies ^
    private boolean enabled = true;
    private Time nextEventTime = Time.ZERO;

    public OrderFactory(Config config, Randoms randoms, Facade facade, DataContext dataContext) {
        this.config = config;
        this.randoms = randoms;
        this.facade = facade;
        this.dataContext = dataContext;
        scheduleEvent(nextEventTime.add(randoms.gaussianTime(
                config.getTime("MeanOrderTime"),
                config.getTime("DeviationOrderTime"))));
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    @Override
    public void scheduleEvent(Time time) {
        nextEventTime = time;
    }

    @Override
    public void process(Time time) {
        if (time.equals(nextEventTime)) {
            Logger.getLogger(getClass()).debug(time + ": New order");

            // Random values
            int xs = randoms.gaussianPositiveInt(config.getInt("MeanX"), config.getInt("DeviationX"));
            int ys = randoms.gaussianPositiveInt(config.getInt("MeanY"), config.getInt("DeviationY"));
            int zs = randoms.gaussianPositiveInt(config.getInt("MeanZ"), config.getInt("DeviationZ"));

            // We need at least one product in order
            if (xs + ys + zs == 0) {
                process(time);
            }

            Order.Type orderType;
            if (randoms.probableBoolean(config.getDouble("UrgencyProbability"))) {
                orderType = Order.Type.URGENT;
            } else {
                orderType = Order.Type.STANDARD;
            }

            // Generate order
            Order order = new Order();
            order.setApprovementTime(time);
            order.setCompletedProducts(0);
            order.setCustomer(dataContext.getUserById(1));
            order.setStatus(Order.Status.APPROVED);
            order.setTotalProducts(xs + ys + zs);
            order.setType(orderType);

            // Generate ordered products
            List<OrderedProduct> products = new ArrayList<OrderedProduct>();
            for (int i = 0; i < xs; i++) {
                OrderedProduct op = new OrderedProduct();
                op.setOrder(order);
                op.setProduct(dataContext.getProductByName("X"));
                products.add(op);
            }
            for (int i = 0; i < ys; i++) {
                OrderedProduct op = new OrderedProduct();
                op.setOrder(order);
                op.setProduct(dataContext.getProductByName("Y"));
                products.add(op);
            }
            for (int i = 0; i < zs; i++) {
                OrderedProduct op = new OrderedProduct();
                op.setOrder(order);
                op.setProduct(dataContext.getProductByName("Z"));
                products.add(op);
            }

            // Move data to Facade
            facade.registerNewOrder(order, products);

            // Schedule next order
            scheduleEvent(nextEventTime.add(randoms.gaussianTime(
                config.getTime("MeanOrderTime"),
                config.getTime("DeviationOrderTime"))));
        }
    }
}
